import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import plotly.express as px
import seaborn as sns
import math
from IPython.display import display
from scipy import stats as st
import requests
import re
from bs4 import BeautifulSoup
Загрузим датасет rest_data, выведем общую информацию и первые десять строк на экран. Проверим на дубликаты.
rest_data = pd.read_csv('/datasets/rest_data.csv')
rest_data.info()
rest_data.head(10)
rest_data.duplicated().sum()
Дататасет состоит из 6 столбцов и 15366 строк. Названия столбцов не требуют изменений. Типы данных корректны. Пропуски, дубликаты отсутствуют.
Посчитаем количество частичных дубликатов по столбцам object_name, chain, object_type, address.
rest_data[['object_name', 'chain', 'object_type', 'address']].duplicated().sum()
Частичные дубликаты составляют 1% датасета, удалим их.
rest_data = rest_data.drop_duplicates(subset=['object_name', 'chain', 'object_type', 'address'])
catering_types = rest_data.groupby('object_type').agg({'id': 'count'}).reset_index().sort_values(
by='id', ascending=False)
catering_types
Построим графики.
plt.figure(figsize=(12, 7))
plt.grid(True)
plt.title('Cоотношение видов объектов общественного питания по количеству')
sns.barplot(x="object_type", y="id", data=catering_types)
ax = plt.gca()
ax.set_ylabel('Количество объектов')
ax.set_xlabel('Вид объекта')
ax.set_xticklabels(catering_types['object_type'], rotation = 90, verticalalignment = 'top');
# x="object_type", y="id", data=catering_types
fig = px.bar(catering_types.sort_values(by='id'), x='id', y='object_type', orientation='h', \
labels={'id':'Количество объектов', 'object_type': 'Вид объекта'})
fig.update_layout(title_text='Cоотношение видов объектов общественного питания по количеству', yaxis_showticklabels=False)
fig.show()
plt.figure(figsize=(12, 12))
plt.pie(catering_types['id'])
plt.legend(catering_types['object_type'])
plt.title('Соотношение видов объектов общественного питания по количеству');
plt.figure(figsize=(20, 10))
plt.title('Соотношение видов объектов общественного питания по количеству')
plt.subplot(1, 2, 1)
plt.grid(True)
sns.barplot(x="id", y="object_type", data=catering_types, orient='h')
ax = plt.gca()
ax.set_xlabel('Количество объектов')
ax.set_ylabel('Вид объекта')
plt.subplot(1, 2, 2)
plt.pie(catering_types['id'])
plt.legend(catering_types['object_type']);
Вывод: самыми популярными объектами общественного питания в Москве являются кафе, столовые, рестораны и фастфуд. Наименее популярными — кафетерии, закусочные и отделы кулинарии в магазинах.
chain_number = rest_data.groupby('chain').agg({'id': 'count'}).reset_index().sort_values(
by='id', ascending=False)
chain_number
Построим график.
plt.figure(figsize=(7, 7))
plt.grid(True)
plt.title('Соотношение сетевых и несетевых заведений по количеству')
sns.barplot(x="chain", y="id", data=chain_number)
ax = plt.gca()
ax.set_ylabel('Количество объектов общественного питания')
ax.set_xlabel('')
ax.set_xticklabels(['несетевое', 'сетевое'])
totals = []
for i in ax.patches:
totals.append(i.get_height())
total = sum(totals)
for i in ax.patches:
ax.text(i.get_x()+.3, i.get_height()-800, str(round((i.get_height()/total)*100, 2))+' %', fontsize=14);
Вывод: несетевых заведений в Москве в четыре раза больше, чем сетевых.
catering_chains = rest_data[rest_data['chain'] == 'да'].groupby('object_type').agg({'id': 'count'}).reset_index().\
sort_values(by='id', ascending=False)
catering_chains
Построим график.
plt.figure(figsize=(12, 7))
plt.grid(True)
plt.title('Распределение сетевых заведений по количеству')
sns.barplot(x="object_type", y="id", data=catering_chains)
ax = plt.gca()
ax.set_ylabel('Количество сетевых заведений')
ax.set_xlabel('Вид объекта')
ax.set_xticklabels(catering_chains['object_type'], rotation = 90, verticalalignment = 'top');
Вывод: больше всего сетей кафе, фастфудов, ресторанов.
chain_types = rest_data.groupby(['chain','object_type']).agg({'id': 'count'}).reset_index()
chain_types['chain'] = chain_types['chain'].replace({'да': 'сетевое', 'нет': 'несетевое'})
chain_types
Построим график.
plt.figure(figsize=(12, 7))
plt.grid(True)
plt.title('Cоотношение видов сетевых и несетевых заведений общественного питания по количеству')
sns.barplot(x="id", y="object_type", hue='chain', data=chain_types, orient='h')
ax = plt.gca()
ax.set_xlabel('Количество объектов')
ax.set_ylabel('Вид объекта');
Создадим таблицу chain_ratio с количеством сетевых заведений. Добавим столбец с количеством несетевых заведений. Найдём соотношение и отсортируем по убыванию.
chain_ratio = chain_types[chain_types['chain'] == 'сетевое']
chain_ratio['id_2'] = chain_types[chain_types['chain'] == 'несетевое'].reset_index()['id']
chain_ratio['ratio'] = chain_ratio['id'] / chain_ratio['id_2']
chain_ratio = chain_ratio.sort_values(by='ratio', ascending=False)
chain_ratio
Построим график.
plt.figure(figsize=(12, 7))
plt.grid(True)
plt.title('Cоотношение видов сетевых и несетевых заведений общественного питания по количеству')
sns.barplot(x="ratio", y="object_type", data=chain_ratio, orient='h')
ax = plt.gca()
ax.set_xlabel('Cоотношение сетевых и несетевых заведений')
ax.set_ylabel('Вид объекта');
Вывод: больше всего сетей фастфудов, кулинарий, ресторанов, кафе.
plt.figure(figsize=(12, 7))
plt.grid(True)
plt.title('Распределение сетевых заведений по количеству мест')
ax = plt.gca()
ax.set_xlabel('Количество мест')
ax.set_ylabel('Количество сетевых заведений')
rest_data[rest_data['chain'] == 'да']['number'].hist(figsize=(12, 7), bins=100);
Вывод: для большинства сетевых заведений характерно небольшое число посадочных мест (до 50).
catering_types_number = rest_data.groupby('object_type').agg({'number': 'mean'}).reset_index().sort_values(
by='number', ascending=False)
catering_types_number
plt.figure(figsize=(12, 7))
plt.grid(True)
plt.title('Распределение среднего количества посадочных мест по типам объектов общественного питания')
sns.barplot(x="object_type", y="number", data=catering_types_number)
ax = plt.gca()
ax.set_ylabel('Cреднее количество посадочных мест')
ax.set_xlabel('Тип объекта общественного питания')
ax.set_xticklabels(catering_types_number['object_type'], rotation = 90, verticalalignment = 'top');
plt.figure(figsize=(12, 7))
plt.grid(True)
plt.title('Распределение среднего количества посадочных мест по типам объектов общественного питания')
sns.stripplot(x="number", y="object_type", data=rest_data, orient='h')
ax = plt.gca()
ax.set_xlabel('Cреднее количество посадочных мест')
ax.set_ylabel('Тип объекта общественного питания');
Вывод: в среднем самое большое количество посадочных мест в столовых и ресторанах.
Создадим таблицу rest_data_chain с сетевыми заведениями. Обработаем столбец object_name. Приведём символы к нижнему регистру методом lower(), заменим ё на е методом replace().
rest_data_chain = rest_data[rest_data['chain'] == 'да'].reset_index()
rest_data_chain['object_name'] = rest_data_chain['object_name'].str.lower()
rest_data_chain['object_name'] = rest_data_chain['object_name'].str.replace('ё','е')
Максимально очистим столбец object_name от лишних подстрок. Создадим массив excess_names.
excess_names = ['«', '»', 'кафе ', 'бар ', 'закусочная ', 'кондитерская ', 'кондитерская-пекарня', 'кофейня ', 'пбо ', \
'пицца ', 'пиццерия ', 'предприятие быстрого обслуживания ', 'ресторан ', 'быстрого питания ', 'ресторан']
В цикле по массиву excess_names удалим подстроки в столбце object_name.
for i in excess_names:
rest_data_chain['object_name'] = rest_data_chain['object_name'].str.replace(i, '')
Заменим наиболее очевидные повторы.
rest_data_chain['object_name'] = rest_data_chain['object_name'].str.replace('бургер бинг burger king','burger king')
rest_data_chain['object_name'] = rest_data_chain['object_name'].str.replace('бургер кинг','burger king')
rest_data_chain['object_name'] = rest_data_chain['object_name'].str.replace('сабвей','subway')
rest_data_chain['object_name'] = rest_data_chain['object_name'].str.replace('старбакс starbucks','starbucks')
rest_data_chain['object_name'] = rest_data_chain['object_name'].str.replace('старбакс','starbucks')
Делаем выборки для проверки.
rest_data_chain.iloc[:, 1:7].sample(5)
Сгруппируем таблицу rest_data_chain_group по названиям сетей, посчитаем количество заведений и среднее число посадочных мест. Сбросим индексы и отсортируем по числу заведений.
rest_data_chain_group = rest_data_chain.groupby('object_name').agg({'id': 'count','number': 'mean'}).reset_index()
rest_data_chain_group = rest_data_chain_group.sort_values(by='id', ascending=False)
rest_data_chain_group.head()
plt.figure(figsize=(12, 7))
plt.grid(True)
plt.title('Точечная диаграмма среднего числа посадочных мест на количество заведений в сети')
sns.scatterplot(data=rest_data_chain_group, x="id", y="number")
ax = plt.gca()
ax.set_xlabel('Количество заведений в сети')
ax.set_ylabel('Среднее число посадочных мест');
Вывод: для сетевых заведений характерно малое число заведений с большим количеством посадочных мест.
rest_data['address_fixed'] = rest_data['address']
Удалим подстроку город Москва, в столбце address_fixed методом str.replace().
rest_data['address_fixed'] = rest_data['address_fixed'].str.replace('город Москва, ', '')
Выделим улицу, разделив строку адреса по подстроке , дом на две части методом split(). Выберем первый элемент массива. Например:
'улица Егора Абакумова, дом 9'.split(', дом ')[0]
Однако среди адресов встречаются не только "дома", но и "корпуса", "владения", "домовладения", которые функция split() не затронет. Например:
'9-я Парковая улица, владение 61А, строение 1'.split(', дом ')[0]
При помощи лямбда-функции разделим каждую строку столбца address_fixed по подстрокам , дом, , корпус, , владение, , домовладение и сохраним первый элемент массива.
rest_data['address_fixed'] = rest_data['address_fixed'].apply(lambda address: address.split(', дом ')[0])
rest_data['address_fixed'] = rest_data['address_fixed'].apply(lambda address: address.split(', корпус ')[0])
rest_data['address_fixed'] = rest_data['address_fixed'].apply(lambda address: address.split(', владение ')[0])
rest_data['address_fixed'] = rest_data['address_fixed'].apply(lambda address: address.split(', домовладение ')[0])
top10_adresses = rest_data['address_fixed'].value_counts().head(10).reset_index()
top10_adresses
Построим график.
plt.figure(figsize=(12, 7))
plt.grid(True)
plt.title('Топ-10 улиц по количеству объектов общественного питания')
sns.barplot(x="index", y="address_fixed", data=top10_adresses)
ax = plt.gca()
ax.set_ylabel('Количество объектов общественного питания')
ax.set_xlabel('Улицы')
ax.set_xticklabels(top10_adresses['index'], rotation = 90, verticalalignment = 'top');
Для использования внешней информации распарсим сайт http://mosopen.ru/regions и поставим в соответствие каждой улице свой административный округ.
URL = 'http://mosopen.ru/district/zelao/streets'
req = requests.get(URL)
soup = BeautifulSoup(req.text, 'lxml')
table = soup.find('div', attrs = {'class':'double_block clearfix'}) # Класс, где хранятся данные
content=[] # Список, в котором будут храниться данные
for row in table.find_all('li'): # Каждая строка обрамляется тегом li, необходимо пробежаться в цикле по всем строкам
content.append([element.text for element in row.find_all('a')]) # И убрать ссылки
content.append(['город Зеленоград']) # Добавим город Зеленоград
Объединим данные в датафрейм, изменим заголовок, приведём всё к нижнему регистру методом lower(), заменим ё на е методом replace(), добавим название округа.
zelao_streets = pd.DataFrame(content)
zelao_streets.columns = ['address_fixed_2']
zelao_streets['address_fixed_2'] = zelao_streets['address_fixed_2'].str.lower()
zelao_streets['address_fixed_2'] = zelao_streets['address_fixed_2'].str.replace('ё','е')
zelao_streets['district_zelao'] = 'ЗелАО'
zelao_streets.head()
URL = 'http://mosopen.ru/district/szao/streets'
req = requests.get(URL)
soup = BeautifulSoup(req.text, 'lxml')
table = soup.find('div', attrs = {'class':'double_block clearfix'}) # Класс, где хранятся данные
content=[] # Список, в котором будут храниться данные
for row in table.find_all('li'): # Каждая строка обрамляется тегом li, необходимо пробежаться в цикле по всем строкам
content.append([element.text for element in row.find_all('a')]) # И убрать ссылки
Объединим данные в датафрейм, изменим заголовок, приведём всё к нижнему регистру методом lower(), заменим ё на е методом replace(), добавим название округа.
szao_streets = pd.DataFrame(content)
szao_streets.columns = ['address_fixed_2']
szao_streets['address_fixed_2'] = szao_streets['address_fixed_2'].str.lower()
szao_streets['address_fixed_2'] = szao_streets['address_fixed_2'].str.replace('ё','е')
szao_streets['district_szao'] = 'СЗАО'
szao_streets.head()
URL = 'http://mosopen.ru/district/zao/streets'
req = requests.get(URL)
soup = BeautifulSoup(req.text, 'lxml')
table = soup.find('div', attrs = {'class':'double_block clearfix'}) # Класс, где хранятся данные
content=[] # Список, в котором будут храниться данные
for row in table.find_all('li'): # Каждая строка обрамляется тегом li, необходимо пробежаться в цикле по всем строкам
content.append([element.text for element in row.find_all('a')]) # И убрать ссылки
Объединим данные в датафрейм, изменим заголовок, приведём всё к нижнему регистру методом lower(), заменим ё на е методом replace(), добавим название округа.
zao_streets = pd.DataFrame(content)
zao_streets.columns = ['address_fixed_2']
zao_streets['address_fixed_2'] = zao_streets['address_fixed_2'].str.lower()
zao_streets['address_fixed_2'] = zao_streets['address_fixed_2'].str.replace('ё','е')
zao_streets['district_zao'] = 'ЗАО'
zao_streets.head()
URL = 'http://mosopen.ru/district/uzao/streets'
req = requests.get(URL)
soup = BeautifulSoup(req.text, 'lxml')
table = soup.find('div', attrs = {'class':'double_block clearfix'}) # Класс, где хранятся данные
content=[] # Список, в котором будут храниться данные
for row in table.find_all('li'): # Каждая строка обрамляется тегом li, необходимо пробежаться в цикле по всем строкам
content.append([element.text for element in row.find_all('a')]) # И убрать ссылки
Объединим данные в датафрейм, изменим заголовок, приведём всё к нижнему регистру методом lower(), заменим ё на е методом replace(), добавим название округа.
uzao_streets = pd.DataFrame(content)
uzao_streets.columns = ['address_fixed_2']
uzao_streets['address_fixed_2'] = uzao_streets['address_fixed_2'].str.lower()
uzao_streets['address_fixed_2'] = uzao_streets['address_fixed_2'].str.replace('ё','е')
uzao_streets['district_uzao'] = 'ЮЗАО'
uzao_streets.head()
URL = 'http://mosopen.ru/district/uao/streets'
req = requests.get(URL)
soup = BeautifulSoup(req.text, 'lxml')
table = soup.find('div', attrs = {'class':'double_block clearfix'}) # Класс, где хранятся данные
content=[] # Список, в котором будут храниться данные
for row in table.find_all('li'): # Каждая строка обрамляется тегом li, необходимо пробежаться в цикле по всем строкам
content.append([element.text for element in row.find_all('a')]) # И убрать ссылки
Объединим данные в датафрейм, изменим заголовок, приведём всё к нижнему регистру методом lower(), заменим ё на е методом replace(), добавим название округа.
uao_streets = pd.DataFrame(content)
uao_streets.columns = ['address_fixed_2']
uao_streets['address_fixed_2'] = uao_streets['address_fixed_2'].str.lower()
uao_streets['address_fixed_2'] = uao_streets['address_fixed_2'].str.replace('ё','е')
uao_streets['district_uao'] = 'ЮАО'
uao_streets.head()
URL = 'http://mosopen.ru/district/uvao/streets'
req = requests.get(URL)
soup = BeautifulSoup(req.text, 'lxml')
table = soup.find('div', attrs = {'class':'double_block clearfix'}) # Класс, где хранятся данные
content=[] # Список, в котором будут храниться данные
for row in table.find_all('li'): # Каждая строка обрамляется тегом li, необходимо пробежаться в цикле по всем строкам
content.append([element.text for element in row.find_all('a')]) # И убрать ссылки
Объединим данные в датафрейм, изменим заголовок, приведём всё к нижнему регистру методом lower(), заменим ё на е методом replace(), добавим название округа.
uvao_streets = pd.DataFrame(content)
uvao_streets.columns = ['address_fixed_2']
uvao_streets['address_fixed_2'] = uvao_streets['address_fixed_2'].str.lower()
uvao_streets['address_fixed_2'] = uvao_streets['address_fixed_2'].str.replace('ё','е')
uvao_streets['district_uvao'] = 'ЮВАО'
uvao_streets.head()
URL = 'http://mosopen.ru/district/vao/streets'
req = requests.get(URL)
soup = BeautifulSoup(req.text, 'lxml')
table = soup.find('div', attrs = {'class':'double_block clearfix'}) # Класс, где хранятся данные
content=[] # Список, в котором будут храниться данные
for row in table.find_all('li'): # Каждая строка обрамляется тегом li, необходимо пробежаться в цикле по всем строкам
content.append([element.text for element in row.find_all('a')]) # И убрать ссылки
Объединим данные в датафрейм, изменим заголовок, приведём всё к нижнему регистру методом lower(), заменим ё на е методом replace(), добавим название округа.
vao_streets = pd.DataFrame(content)
vao_streets.columns = ['address_fixed_2']
vao_streets['address_fixed_2'] = vao_streets['address_fixed_2'].str.lower()
vao_streets['address_fixed_2'] = vao_streets['address_fixed_2'].str.replace('ё','е')
vao_streets['district_vao'] = 'ВАО'
vao_streets.head()
URL = 'http://mosopen.ru/district/svao/streets'
req = requests.get(URL)
soup = BeautifulSoup(req.text, 'lxml')
table = soup.find('div', attrs = {'class':'double_block clearfix'}) # Класс, где хранятся данные
content=[] # Список, в котором будут храниться данные
for row in table.find_all('li'): # Каждая строка обрамляется тегом li, необходимо пробежаться в цикле по всем строкам
content.append([element.text for element in row.find_all('a')]) # И убрать ссылки
Объединим данные в датафрейм, изменим заголовок, приведём всё к нижнему регистру методом lower(), заменим ё на е методом replace(), добавим название округа.
svao_streets = pd.DataFrame(content)
svao_streets.columns = ['address_fixed_2']
svao_streets['address_fixed_2'] = svao_streets['address_fixed_2'].str.lower()
svao_streets['address_fixed_2'] = svao_streets['address_fixed_2'].str.replace('ё','е')
svao_streets['district_svao'] = 'СВАО'
svao_streets.head()
URL = 'http://mosopen.ru/district/sao/streets'
req = requests.get(URL)
soup = BeautifulSoup(req.text, 'lxml')
table = soup.find('div', attrs = {'class':'double_block clearfix'}) # Класс, где хранятся данные
content=[] # Список, в котором будут храниться данные
for row in table.find_all('li'): # Каждая строка обрамляется тегом li, необходимо пробежаться в цикле по всем строкам
content.append([element.text for element in row.find_all('a')]) # И убрать ссылки
Объединим данные в датафрейм, изменим заголовок, приведём всё к нижнему регистру методом lower(), заменим ё на е методом replace(), добавим название округа.
sao_streets = pd.DataFrame(content)
sao_streets.columns = ['address_fixed_2']
sao_streets['address_fixed_2'] = sao_streets['address_fixed_2'].str.lower()
sao_streets['address_fixed_2'] = sao_streets['address_fixed_2'].str.replace('ё','е')
sao_streets['district_sao'] = 'САО'
sao_streets.head()
URL = 'http://mosopen.ru/district/cao/streets'
req = requests.get(URL)
soup = BeautifulSoup(req.text, 'lxml')
table = soup.find('div', attrs = {'class':'double_block clearfix'}) # Класс, где хранятся данные
content=[] # Список, в котором будут храниться данные
for row in table.find_all('li'): # Каждая строка обрамляется тегом li, необходимо пробежаться в цикле по всем строкам
content.append([element.text for element in row.find_all('a')]) # И убрать ссылки
Объединим данные в датафрейм, изменим заголовок, приведём всё к нижнему регистру методом lower(), заменим ё на е методом replace(), добавим название округа.
cao_streets = pd.DataFrame(content)
cao_streets.columns = ['address_fixed_2']
cao_streets['address_fixed_2'] = cao_streets['address_fixed_2'].str.lower()
cao_streets['address_fixed_2'] = cao_streets['address_fixed_2'].str.replace('ё','е')
cao_streets['district_cao'] = 'ЦАО'
cao_streets.head()
Создадим столбец address_fixed_2 в таблице rest_data. Приведём всё к нижнему регистру методом lower() и заменим ё на е методом replace().
rest_data['address_fixed_2'] = rest_data['address_fixed']
rest_data['address_fixed_2'] = rest_data['address_fixed_2'].str.lower()
rest_data['address_fixed_2'] = rest_data['address_fixed_2'].str.replace('ё','е')
Для объединения данных максимально очистим столбец address_fixed_2 от лишних подстрок. Создадим массив excess_adress со всеми возможными комбинациями.
excess_adress = [', улица', ' улица', 'улица ', \
', проспект', ' проспект', 'проспект ', \
', проезд', ' проезд', 'проезд ', \
', бульвар', ' бульвар', 'бульвар ', \
', площадь', ' площадь', 'площадь ', \
', линия', ' линия', 'линия ', \
', переулок', ' переулок', 'переулок ', \
', квартал', ' квартал', 'квартал ', \
', шоссе', ' шоссе', 'шоссе ', \
', просека', ' просека', 'просека ', ', просек', ' просек', 'просек ', \
', аллея', ' аллея', 'аллея ', \
', территория', ' территория', 'территория ', \
', тупик', ' тупик', 'тупик ', \
' 11-й', '11-й ', ' 11-я', '11-я ', ' 12-й', '12-й ', ' 12-я', '12-я ', \
' 13-й', '13-й ', ' 13-я', '13-я ', ' 14-й', '14-й ', ' 14-я', '14-я ', \
' 15-й', '15-й ', ' 15-я', '15-я ', ' 16-й', '16-й ', ' 16-я', '16-я ', \
' 17-й', '17-й ', ' 17-я', '17-я ', ' 17-й', '17-й ', ' 17-я', '17-я ', \
' 19-й', '19-й ', ' 19-я', '19-я ', ' 20-й', '20-й ', ' 20-я', '20-я ', \
' 1-й', '1-й ', ' 1-я', '1-я ', ' 2-й', '2-й ', ' 2-я', '2-я ', ' 3-й', '3-й ', ' 3-я', '3-я ', \
' 4-й', '4-й ', ' 4-я', '4-я ', ' 5-й', '5-й ', ' 5-я', '5-я ', ' 6-й', '6-й ', ' 6-я', '6-я ', \
' 7-й', '7-й ', ' 7-я', '7-я ', ' 8-й', '8-й ', ' 8-я', '8-я ', ' 9-й', '9-й ', ' 9-я', '9-я ']
В цикле по массиву excess_adress удалим подстроки в столбце address_fixed_2 каждой таблицы.
for i in excess_adress:
rest_data['address_fixed_2'] = rest_data['address_fixed_2'].str.replace(i, '')
zelao_streets['address_fixed_2'] = zelao_streets['address_fixed_2'].str.replace(i, '')
szao_streets['address_fixed_2'] = szao_streets['address_fixed_2'].str.replace(i, '')
zao_streets['address_fixed_2'] = zao_streets['address_fixed_2'].str.replace(i, '')
uzao_streets['address_fixed_2'] = uzao_streets['address_fixed_2'].str.replace(i, '')
uao_streets['address_fixed_2'] = uao_streets['address_fixed_2'].str.replace(i, '')
uvao_streets['address_fixed_2'] = uvao_streets['address_fixed_2'].str.replace(i, '')
vao_streets['address_fixed_2'] = vao_streets['address_fixed_2'].str.replace(i, '')
svao_streets['address_fixed_2'] = svao_streets['address_fixed_2'].str.replace(i, '')
sao_streets['address_fixed_2'] = sao_streets['address_fixed_2'].str.replace(i, '')
cao_streets['address_fixed_2'] = cao_streets['address_fixed_2'].str.replace(i, '')
Наконец объединим данные методом merge() по столбцу address_fixed_2.
rest_data = rest_data.merge(zelao_streets, on='address_fixed_2', how='left')\
.merge(szao_streets, on='address_fixed_2', how='left')\
.merge(zao_streets, on='address_fixed_2', how='left')\
.merge(uzao_streets, on='address_fixed_2', how='left')\
.merge(uao_streets, on='address_fixed_2', how='left')\
.merge(uvao_streets, on='address_fixed_2', how='left')\
.merge(vao_streets, on='address_fixed_2', how='left')\
.merge(svao_streets, on='address_fixed_2', how='left')\
.merge(sao_streets, on='address_fixed_2', how='left')\
.merge(cao_streets, on='address_fixed_2', how='left')
Удалим дубликаты.
rest_data = rest_data.drop_duplicates()
Делаем выборки для проверки.
rest_data.iloc[:, 7:18].sample(5)
Примечание: некоторые улицы, например, Талалихина находятся на территории сразу двух округов: Центрального и Юго-Восточного. Ленинский проспект раскинулся сразу на четыре округа: Юго-Западный, Южный, Западный и Центральный округа. Бывает, что улицы со схожим названием после обработки оказываются сразу в двух округах. Например, Парковая улица из Западного округа и 1-я Парковая улица из Восточного округа.
Посчитаем количество улиц, расположенных на территории каждого округа, сбросим индексы, изменим заголовки, добавим названия округов и отсортируем по количеству улиц.
top10_districts = rest_data.iloc[:, 8:18].count().reset_index()
top10_districts.columns = ['id', 'streets']
top10_districts['district'] = pd.Series(['ЗелАО', 'СЗАО', 'ЗАО', 'ЮЗАО', 'ЮАО', 'ЮВАО', 'ВАО', 'СВАО', 'САО', 'ЦАО'])
top10_districts = top10_districts.sort_values(by='streets', ascending=False)
top10_districts
Построим график.
plt.figure(figsize=(12, 7))
plt.grid(True)
plt.title('Топ-10 округов по количеству объектов общественного питания')
sns.barplot(x="district", y="streets", data=top10_districts)
ax = plt.gca()
ax.set_ylabel('Количество объектов общественного питания')
ax.set_xlabel('Округа');
Посчитаем количество улиц, оказавшихся за пределами округов. Просуммируем количество ненулевых элементов по строкам при помощи функции sum() с параметром axis=1 и посчитаем количество уникальных элементов.
rest_data.iloc[:, 8:18].notnull().sum(axis=1).value_counts()
Улиц вне округа — 814. Улиц, расположенных более чем на территории более чем двух округов (в т.ч. ошибочно), — 2306.
Посмотрим на улицы вне округов. Объединим в запросе пропуски и сделаем выборку методом sample.
rest_data.query('\
district_zelao.isnull() and \
district_szao.isnull() and \
district_zao.isnull() and \
district_uzao.isnull() and \
district_uao.isnull() and \
district_uvao.isnull() and \
district_vao.isnull() and \
district_svao.isnull() and \
district_sao.isnull() and \
district_cao.isnull() \
').sample(3)
Видно, что это Подмосковье: Зеленоград, Московский, Троицк, Щербинка, а также поселения и микрорайоны.
Примечание: некоторые улицы, например, академика Ландау или Маршала Прошлякова отсутствуют на сайте http://mosopen.ru.
Найдём, в каких районах Москвы находятся топ-10 улиц по количеству объектов общественного питания. Создадим пустой датафрейм top10_adresses_districts с заголовками.
top10_adresses_districts = pd.DataFrame(
columns=['district_zelao', 'district_szao', 'district_zao', 'district_uzao', 'district_uao', \
'district_uvao', 'district_vao', 'district_svao', 'district_sao', 'district_cao']
)
top10_adresses_districts
В цикле по каждой улице найдём в таблице rest_data первое совпадение по улице и добавим информацию по округам.
for i in top10_adresses['index']:
top10_adresses_districts = top10_adresses_districts.append(
rest_data[rest_data['address_fixed'] == i].iloc[:, 8:18].head(1), ignore_index=True)
top10_adresses_districts
Посчитаем количество округов, добавим названия и отсортируем по убыванию.
top10_adresses_districts = top10_adresses_districts.count().reset_index()
top10_adresses_districts.columns = ['id', 'streets']
top10_adresses_districts['district'] = pd.Series(
['ЗелАО', 'СЗАО', 'ЗАО', 'ЮЗАО', 'ЮАО', 'ЮВАО', 'ВАО', 'СВАО', 'САО', 'ЦАО'])
top10_adresses_districts = top10_adresses_districts.sort_values(by='streets', ascending=False)
top10_adresses_districts
Построим график.
plt.figure(figsize=(12, 7))
plt.grid(True)
plt.title('Топ-10 популярных округов')
sns.barplot(x="district", y="streets", data=top10_adresses_districts)
ax = plt.gca()
ax.set_ylabel('Количество объектов общественного питания')
ax.set_xlabel('Округа');
Вывод: больше всего объектов общественного питания в Центральном, Западном, Юго-Западном и Южном округах города Москвы.
Сравним с ценами на недвижимость по данным сайта: https://www.irn.ru/rating/moscow/?segment=areas¶m=IS&sort=value¤cy=0
Центральный, Юго-Западный и Западный округи самые дорогие. На четвёртом месте Северо-Западный вместо Южного. Связано ли количество объектов общественного питания в Южном округе с тем, что он является крупнейшим по численности населения?
not_popular_streets = rest_data['address_fixed'].value_counts().reset_index()
not_popular_streets.columns = ['street', 'number']
not_popular_streets = not_popular_streets.query('number == 1')
not_popular_streets.head()
Найдём, в каких районах Москвы находятся улицы с одним объектом общественного питания. Создадим пустой датафрейм not_popular_streets_districts с заголовками.
not_popular_streets_districts = pd.DataFrame(
columns=['district_zelao', 'district_szao', 'district_zao', 'district_uzao', 'district_uao', \
'district_uvao', 'district_vao', 'district_svao', 'district_sao', 'district_cao']
)
not_popular_streets_districts
В цикле по каждой улице найдём в таблице rest_data первое совпадение по улице и добавим информацию по округам.
for i in not_popular_streets['street']:
not_popular_streets_districts = not_popular_streets_districts.append(
rest_data[rest_data['address_fixed'] == i].iloc[:, 8:18].head(1), ignore_index=True)
not_popular_streets_districts.head()
Посчитаем количество округов, сбросим индексы, изменим заголовки, добавим названия и отсортируем по убыванию.
not_popular_streets_districts = not_popular_streets_districts.count().reset_index()
not_popular_streets_districts.columns = ['id', 'streets']
not_popular_streets_districts['district'] = pd.Series(
['ЗелАО', 'СЗАО', 'ЗАО', 'ЮЗАО', 'ЮАО', 'ЮВАО', 'ВАО', 'СВАО', 'САО', 'ЦАО'])
not_popular_streets_districts = not_popular_streets_districts.sort_values(by='streets', ascending=False)
not_popular_streets_districts
Построим график.
plt.figure(figsize=(12, 7))
plt.grid(True)
plt.title('Топ-10 непопулярных округов')
sns.barplot(x="district", y="streets", data=not_popular_streets_districts)
ax = plt.gca()
ax.set_ylabel('Количество объектов общественного питания')
ax.set_xlabel('Округа');
Вывод: самыми непопулярными являются Северо-Восточный, Восточный, Юго-Восточный и Северный округа.
Примечание: исключил Центральный округ, потому что там больше всего объектов общественного питания, стало быть, больше всего улиц с одним объектом общественного питания.
Посмотрим на распределение количества посадочных мест для улиц с большим количеством объектов общественного питания.
Для этого создадим пустой объект Series top_10_numbers. В цикле по каждой из топ-10 улиц найдём в таблице rest_data совпадение по улице и добавим количество посадочных мест в top_10_numbers.
top_10_numbers = pd.Series()
top_10_numbers
for i in top10_adresses['index']:
top_10_numbers = top_10_numbers.append(rest_data[rest_data['address_fixed'] == i].loc[:, 'number'])
Построим гистограмму, ограничив количество посадочных мест 250.
plt.figure(figsize=(12, 7))
plt.grid(True)
plt.title('Распределение количества посадочных мест для улиц с большим количеством объектов общественного питания')
ax = plt.gca()
ax.set_xlabel('Количество мест')
ax.set_ylabel('Количество заведений')
top_10_numbers[top_10_numbers < 250].hist(figsize=(12, 7), bins=100);
Вывод: для улиц с большим количеством объектов общественного питания характерно небольшое число посадочных мест (до 50).
Налицо географическая стратификация Москвы: элитный Центр, богатые Запад и Юго-Запад, бедные Северо-Восток, Восток и Юго-Восток, средние Север, Северо-Запад, Юг.
https://newizv.ru/news/city/01-08-2019/novaya-karta-moskvy-pokazala-sotsialnoe-rassloenie-zhiteley
Самыми популярными объектами общественного питания в Москве являются кафе, столовые, рестораны и фастфуд. Наименее популярными — кафетерии, закусочные и отделы кулинарии в магазинах.
Несетевых заведений в Москве в четыре раза больше, чем сетевых.
Больше всего сетей кафе, фастфудов, ресторанов.
Для большинства сетевых заведений характерно небольшое число посадочных мест (до 50).
Больше всего объектов общественного питания в Центральном, Западном, Юго-Западном и Южном округах города Москвы.
Самыми непопулярными являются Северо-Восточный, Восточный, Юго-Восточный и Северный округа.
Для улиц с большим количеством объектов общественного питания характерно небольшое число посадочных мест (до 50).
Рекомендации: кафе, ресторан, фастфуд с небольшим числом посадочных мест (до 50) в Центральном, Западном, Юго-Западном округах.